using System;
using System.ComponentModel;
using System.Data.OleDb;
using System.Diagnostics;
using AstFramework.Model;
using Vulcan.Utility.ComponentModel;

namespace VulcanEngine.IR.Ast.Table
{
    public partial class AstTableColumnBaseNode
    {
        protected AstTableColumnBaseNode(IFrameworkItem parentAstNode)
            : base(parentAstNode)
        {
            InitializeAstNode();

            SingletonPropertyChanged += AstTableColumnBaseNode_SingletonPropertyChanged;
        }

        // TODO: Need a preview event to implement computed and assignable correctly
        private void AstTableColumnBaseNode_SingletonPropertyChanged(object sender, VulcanPropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Computed")
            {
                VulcanOnPropertyChanged("IsComputed", IsComputed, IsComputed);
                VulcanOnPropertyChanged("IsAssignable", IsAssignable, IsAssignable);
            }
        }

        private bool _identity;
        private bool _identitySet;
        private bool _isAssignable;
        private bool _isAssignableSet;

        public bool IsAutoGenerated { get; set; }

        [BrowsableAttribute(true)]
        [VulcanCategoryAttribute("Read Only")]
        [VulcanDescriptionAttribute("Specifies IsComputed")]
        public abstract bool IsComputed { get; }

        [BrowsableAttribute(true)]
        [VulcanCategoryAttribute("Read Only")]
        [VulcanDescriptionAttribute("Specifies IsAssignable")]
        public virtual bool IsAssignable
        {
            // TODO: Timestamp2?
            get
            {
                if (!_isAssignableSet)
                {
                    _isAssignable = !IsIdentityColumn;
                    _isAssignableSet = true;
                }
                return _isAssignable;
            }
            set
            {
                _isAssignable = value;
                _isAssignableSet = true;
            }
        }

        public bool IsIdentityColumn
        {
            get
            {
                if (_identitySet)
                {
                    return _identity;
                }

                AstTableNode tableNode = FirstParent<AstTableNode>();

                // TODO: Shouldn't need a null check here.  Why would a parent table not be found?
                if (tableNode != null)
                {
                    foreach (AstTableKeyBaseNode key in tableNode.Keys)
                    {
                        if (key is AstTableIdentityNode)
                        {
                            foreach (AstTableKeyColumnNode keyColumn in key.Columns)
                            {
                                if (this.Equals(keyColumn.Column))
                                {
                                    _identitySet = true;
                                    _identity = true;
                                }
                            }
                        }
                    }
                }

                return _identity;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"), Browsable(false)]
        public string DefaultValue
        {
            get
            {
                if (!String.IsNullOrEmpty(Default))
                {
                    return Default;
                }

                switch (ColumnType)
                {
                    case ColumnType.Binary:
                    case ColumnType.Byte:
                    case ColumnType.Currency:
                    case ColumnType.Decimal:
                    case ColumnType.Double:
                    case ColumnType.Single:
                    case ColumnType.Int32:
                    case ColumnType.Int64:
                    case ColumnType.UInt32:
                    case ColumnType.UInt64:
                    case ColumnType.SByte:
                    case ColumnType.VarNumeric:
                        return "0";
                    case ColumnType.Boolean:
                        return false.ToString();
                    case ColumnType.Xml:
                    case ColumnType.Guid:
                        return "NEWID()";
                    case ColumnType.Object:
                        return "NULL";
                    case ColumnType.Date:
                    case ColumnType.DateTime:
                    case ColumnType.Time:
                    case ColumnType.DateTime2:
                    case ColumnType.DateTimeOffset:
                        return new DateTime().ToString();
                    case ColumnType.AnsiString:
                    case ColumnType.AnsiStringFixedLength:
                        return "\'Unknown\'";
                    case ColumnType.String:
                    case ColumnType.StringFixedLength:
                        return "N\'Unknown\'";
                    default:
                        return "0";
                }
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db", Justification = "'Db' is required by FXCop.")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Fallthroughs required for understandability of code.")]
        public static ColumnType ConvertOleDbTypeToColumnType(OleDbType type)
        {
            switch (type)
            {
                case OleDbType.Boolean:
                    return ColumnType.Boolean;

                case OleDbType.VarWChar:
                case OleDbType.LongVarWChar:
                    return ColumnType.String;
                case OleDbType.WChar:
                    return ColumnType.StringFixedLength;

                case OleDbType.Char:
                    return ColumnType.AnsiStringFixedLength;
                case OleDbType.BSTR:
                case OleDbType.VarChar:
                case OleDbType.LongVarChar:
                    return ColumnType.AnsiString;

                case OleDbType.Double:
                    return ColumnType.Double;

                case OleDbType.Currency:
                    return ColumnType.Currency;
                case OleDbType.Numeric:
                case OleDbType.Decimal:
                    return ColumnType.Decimal;

                case OleDbType.Integer:
                    return ColumnType.Int32;

                case OleDbType.BigInt:
                    return ColumnType.Int64;

                case OleDbType.UnsignedInt:
                    return ColumnType.UInt32;

                case OleDbType.UnsignedBigInt:
                    return ColumnType.UInt64;

                case OleDbType.Date:
                case OleDbType.DBDate:
                    return ColumnType.Date;

                case OleDbType.DBTime:
                case OleDbType.Filetime:
                    return ColumnType.Time;

                case OleDbType.DBTimeStamp:
                    return ColumnType.DateTime;

                case OleDbType.Binary:
                    return ColumnType.Binary;

                case OleDbType.Guid:
                    return ColumnType.Guid;

                case OleDbType.SmallInt:
                    return ColumnType.Int16;

                case OleDbType.UnsignedTinyInt:
                    return ColumnType.UInt16;

                default:
                    Debug.Assert(false, "The OleDbType, " + type + ", lacks a matching ColumnType.");
                    return ColumnType.Object;
            }
        }
    }
}
